package com.apobates.forum.orize.core.plug;

import com.apobates.forum.orize.OrizeResource;
import com.apobates.forum.orize.OrizeResources;
import com.apobates.forum.orize.core.OrizeResourceLoader;
import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.eclipse.persistence.jaxb.UnmarshallerProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import javax.xml.bind.*;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import java.util.Collection;
import java.util.Collections;

/**
 * JSON文件中定义资源
 * JSON文件结构示例:
 * {
 *  "resources" : {
 *    "stamp" : "20210805",
 *    "item" : [ {
 *      "path" : "/orders/list",
 *      "method" : "get",
 *      "action" : "VIEW",
 *      "roles" : "Member",
 *      "spot" : "do"
 *    }, ...
 *    ]
 *   }
 * }
 * @author xiaofanku@live.cn
 * @since 20210822
 */
public class OrizeResourceJsonLoader implements OrizeResourceLoader {
    private final String jsonFilePath;
    private final static Logger logger = LoggerFactory.getLogger(OrizeResourceJsonLoader.class);

    static {
        logger.info("[OrizeResourceJsonLoader]set global system property");
        System.setProperty("javax.xml.bind.context.factory","org.eclipse.persistence.jaxb.JAXBContextFactory");
    }

    public OrizeResourceJsonLoader(String jsonFilePath) {
        this.jsonFilePath = jsonFilePath;
    }

    public Document load() {
        try {
            Collection<OrizeResource> rss = unmarshal(jsonFilePath);
            return buildDocument(rss);
        }catch (Exception e){
            if(logger.isDebugEnabled()){
                logger.debug(e.getMessage(), e);
            }
        }
        throw new IllegalStateException("解析JSON资源定义失败");
    }

    /**
     *
     * @param data
     * @return
     * @throws JAXBException
     * @throws ParserConfigurationException
     */
    private Document buildDocument(Collection<OrizeResource> data) throws JAXBException, ParserConfigurationException {
        OrizeResources rss = new OrizeResources(System.currentTimeMillis()+"", data);
        // Convert the Domain Model to XML
        JAXBContext jaxbContext = JAXBContext.newInstance(OrizeResources.class);

        Marshaller marshaller = jaxbContext.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        JAXBElement<OrizeResources> jaxbElement = new JAXBElement<OrizeResources>(new QName(null, "resources"), OrizeResources.class, rss);
        // Create the Document
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document document = db.newDocument();
        // to w3c document
        marshaller.marshal(jaxbElement, document);
        return document;
    }

    //see:://examples.javacodegeeks.com/core-java/xml/bind/jaxb-json-example/
    public static void marshal(OrizeResources rss, String jsonFilePath) throws JAXBException {
        // Create a JaxBContext
        JAXBContext jc = JAXBContext.newInstance(OrizeResources.class);

        // Create the Marshaller Object using the JaxB Context
        Marshaller marshaller = jc.createMarshaller();

        // Set the Marshaller media type to JSON or XML
        marshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json");

        // Set it to true if you need to include the JSON root element in the JSON output
        marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, true);

        // Set it to true if you need the JSON output to formatted
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        //Marshal the employees list in file
        marshaller.marshal(rss, new File(jsonFilePath));
    }

    private static Collection<OrizeResource> unmarshal(String jsonFilePath) throws JAXBException {
        // Create a JaxBContext
        JAXBContext jc = JAXBContext.newInstance(OrizeResources.class);

        // Create the Unmarshaller Object using the JaxB Context
        Unmarshaller unmarshaller = jc.createUnmarshaller();

        // Set the Unmarshaller media type to JSON or XML
        unmarshaller.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json");

        // Set it to true if you need to include the JSON root element in the
        // JSON input
        unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, true);

        //Create the StreamSource by creating StringReader using the JSON input
        StreamSource json = new StreamSource(new File(jsonFilePath));
        OrizeResources rss = unmarshaller.unmarshal(json, OrizeResources.class).getValue();

        //
        try {
            return rss.getResources();
        } catch (Exception e) {
            return Collections.emptyList();
        }

    }
}
